/*
 * DEBC1.c
 *
 *  Created on: 17/11/2012
 *      Author: Richard
 */

#include <string.h>

#include "DEBC1.h"
#include "DEBCI.h"

#include "../../sensor.h"
#include "../SWTIMER/SWTIMERC1.h"
#include "../UI/BUTTON/BTNC1.h"

static SWTIM_tstTimer DEB__stTimer;

static struct
{
   uint16 u16DebounceCounter   :13;
   uint16 boCurrentState       :1;
   uint16 boLastState          :1;
   uint16 boLastCallbackState  :1;
} DEB__astStateData[DEB_nenNumberOfInputs];

static const struct
{
   DIO_tenLogicLevel   (*pfGetInput)(DIO_tenPinName);
   DIO_tenPinName      enPinName;
   DIO_tenLogicLevel   enActiveLevel;
   uint16              u16DebounceTime;
   void                (*pfCallback)(DEB_tstDebounceEvent * pstEventInfo);
} DEB__astDebounceData[DEB_nenNumberOfInputs] =
{
/* DEB_nenButton */ { &DIO_enGetInput, DIO_nenButton, tLo, 5, &BTN_vPressEvent }
};

static void DEB__vTimerCallback( SWTIM_tstTimer *pstTimer );

void DEB_vReset( void )
{
   memset( (void*)DEB__astStateData, 0, sizeof(DEB__astStateData) );

   DEB__stTimer.interval = DEB__nDebounceTimeInterval;
   DEB__stTimer.mode = SWTIM_nenPeriodicMode;
   DEB__stTimer.handler = &DEB__vTimerCallback;
}

/* Called from interrupt context */
void DEB_vSetAppRequest( DIO_tenPinName enPin )
{
   /* should always be DIO_nenButton, but check anyway */
   if( enPin == DIO_nenButton )
   {
      APP_unRequests.F.biDebounce = 1;
   }
}

/* called from the main wakeup task */
bool DEB_boStartDebounce( void )
{
   SWTIM_vTimerStart( &DEB__stTimer );
   return True;
}

/* called from the SWTIMER callback, interrupt context */
void DEB__vTimerCallback( SWTIM_tstTimer *pstTimer )
{
   DEB_tenInput enInput;
   DIO_tenLogicLevel enLevel;
   DEB_tstDebounceEvent stEventInfo;

   (void)pstTimer;

   for( enInput = (DEB_tenInput)( DEB_nenNumberOfInputs - 1); enInput >= DEB_nenFirstInput; enInput-- )
   {
      /* 10ms passed, read the IO level */
      if( DEB__astDebounceData[enInput].enPinName < DIO_nenNumberOfPins )
      {
         if( DEB__astDebounceData[enInput].pfGetInput != NULL )
         {
            enLevel = DEB__astDebounceData[enInput].pfGetInput(DEB__astDebounceData[enInput].enPinName);
            enLevel = ( enLevel == DEB__astDebounceData[enInput].enActiveLevel ) ? tHi : tLo;

            if( DEB__astStateData[enInput].u16DebounceCounter < DEB__astDebounceData[enInput].u16DebounceTime )
            {
               DEB__astStateData[enInput].u16DebounceCounter++;
            }

            if( enLevel != DEB__astStateData[enInput].boLastState)
            {
               DEB__astStateData[enInput].u16DebounceCounter = 0;
            }

            if( DEB__astStateData[enInput].u16DebounceCounter == DEB__astDebounceData[enInput].u16DebounceTime )
            {
               DEB__astStateData[enInput].boCurrentState = enLevel;
               /* stop timer */
               SWTIM_vTimerStop( &DEB__stTimer );

               if( DEB__astStateData[enInput].boCurrentState != DEB__astStateData[enInput].boLastCallbackState )
               {
                  DEB__astStateData[enInput].boLastCallbackState = enLevel;

                  /* run callback */
                  if( DEB__astDebounceData[enInput].pfCallback != NULL )
                  {
                     stEventInfo.enInput = enInput;
                     stEventInfo.enLogicLevel = (DIO_tenLogicLevel)DEB__astStateData[enInput].boCurrentState;
                     DEB__astDebounceData[enInput].pfCallback( &stEventInfo );
                  }
               }

               /* increment counter so this if only runs once */
               DEB__astStateData[enInput].u16DebounceCounter++;
            }
            else if( DEB__astStateData[enInput].u16DebounceCounter == ( DEB__astDebounceData[enInput].u16DebounceTime + 1 ) )
            {
               /* if debounce was started, but there is no difference - stop the timer, no more processing required */
               SWTIM_vTimerStop( &DEB__stTimer );
            }

            DEB__astStateData[enInput].boLastState = enLevel;
         }
      }
   }
}

DIO_tenLogicLevel DEB_enGetLevel( DEB_tenInput enInput )
{
   return (DIO_tenLogicLevel)DEB__astStateData[enInput].boCurrentState;
}
